:- module(database_access, [
%        db_key/1,
	resource_description/2,
% The two below are somewhat special; think about their types!
	resource_needs/2,
	resource_provides/2,
%
	resource_db_update/2,
	resource_delete/1,
%
	organization_description/2,
	organization_db_update/2,
	organization_delete/1,
%
	user_description/2,
	user_db_update/2,
	user_delete/1,
%
	certification_description/2,
	certification_db_update/2,
	certification_delete/1,
%
	ditem_description/2,
	ditem_db_update/2,

%  Think more carefully on this one!
	ditem_exists/1,
	ditem_delete/1
			   ], [assertions,basicmodes,regtypes]).

:- comment(title, "Application-Oriented Database Interface").
:- comment(subtitle, "AMOS Project").

:- comment(author, "Jes@'{u}s Correas, Manuel Carro, and the CLIP Group").
:- comment(author, "Facultad de Inform@'{a}tica").
:- comment(author, "Universidad Polit@'{e}cnica de Madrid").
:- comment(author, "@tt{clip@@dia.fi.upm.es}").
:- comment(author, "@tt{http://www.clip.dia.fi.upm.es/}").

:- comment(module, "

This module implements the predicates which perform queries and
modifications on data stored in the database implementation of the
AMOS ontology.  The primitives defined in this module aim at being
implementation-independent: the underlying database technology,
schemata, and vendor or query and data language variant should not be
reflected at this level.  Likewise, the Prolog-level implementation of
the data handled by the database interface should not be seen at this
level.  This is achieved by wrapping each ontology class with a module
treating the class instantiations as opaque data structures.  The
operations provided by these modules are internally used when needed
by the database access module.

Unlike the modules related with ontology classes, which have a
declarative semantics based on the generation of one state from the
previous one, the predicates related to updates in this module are
aware of the existence of an (external) state which lives across calls
(and, ultimately, across program executions).  Therefore, they do not
make the distinction of input and output (database) states: changes to
the contents of the database are implicit, and states previous to a
change are not recoverable on backtracking.

In addition to the operations to consult and store, naturally induced
by the ontology, some utility operations, mainly related to accessing
the terms needed to perform a search, are included in this module.
This is so in order for the matching engine to have a faster access to
the information needed to perform searches, and to make the code of
the matching engine simpler and clearer.


In order to provide a better integration of the database access
primitives with the operational semantics of Prolog, data retrieval
operations can both consult and generate answers on backtracking.
This makes it easier to use them from within the matching engine as
well.

@sp

@include{../Doc/acknowledgements.lpdoc}

@newpage
").

%% ==================================================================
%% Modules for ontology classes access.
:- use_module(db(asset_access),[
	asset/1,
	asset_name/3,
	asset_author/3,
	asset_maintained_by/3,
	asset_description/3,
	asset_submitted_by/3,
	asset_certification/3,
	asset_check/2,
	asset_update/3
			   ]).
:- use_module(db(resource_access),[
	resource/1,
	resource_asset/3,
	resource_requires/3,
	resource_identificationTags/3,
	resource_uses/3,
	resource_language/3,
	resource_certification_status/3,
	resource_check/2,
	resource_update/3
			      ]).
:- use_module(db(organization_access),[
	organization/1,
	organization_name/3,
	organization_check/2,
	organization_update/3
				  ]).
:- use_module(db(user_access),[
	user/1,
	user_affiliation/3,
	user_check/2,
	user_update/3
			  ]).
:- use_module(db(certification_access),[
	certification/1,
	certification_check/2,
	certification_update/3
				   ]).
:- use_module(db(dictionary_access),[
	ditem/1,
	ditem_entry/3,
	ditem_definition/3,
	ditem_synonyms/3,
	ditem_generalization/3,
	ditem_translation/3,
	ditem_check/2,
	ditem_update/3
				]).

:- use_module(db(database_files),[
	save_element/2
				  ]).

:- use_module(library(aggregates),[bagof/3,findall/3,(^)/2]).
:- use_module(library(lists), [length/2]).
:- use_module(db(basic_types),[null/1]).

%% ==================================================================
%% Module for actual database access.
:- use_module(db(database_impl)).

:- reexport(db(database_impl),[db_key/1]).
%% ==================================================================

:- pred data_fields(?Functor,?Arity,?Basic_fields) 

# "internal db representation of different ontology
  objects. @var{Functor} is the name of the structure in the database;
  @var{Arity} is the arity of the structure in the database;
  @var{Basic_fields} is a list with the names of the basic fields in
  the high-level ontology representation (@tt{*_access.pl} source
  files). These fields will be located in the rightmost arguments of
  the structure @var{Functor}/@var{Arity}.".

data_fields(resource,6,
	[
	    size,
	    versionof,
	    notes
	]).
                                 % key,assetKey,languageKey,resource:*
data_fields(asset,19,
	[
	    name,
	    homepage,
	    downloadpage,
	    contact,
	    license,
	    licenseurl,
	    version,
	    references,
	    constraints,
	    freedom,
	    creationdate,
	    submissiondate,
	    environment,
	    cost,
	    security,
	    signature
	]).
                                 % key,submKey,certKey,asset:*
data_fields(organization,2,[name]).
data_fields(user,3,[signature]). % key,organizationKey,signature
data_fields(certification,10,
	[
	    date,
	    status,
	    level,
	    policy,
	    reference,
	    artifact_type,
	    artifact,
	    verifier,
	    comments
	]).
data_fields(ditem,3,[entry,definition]).

%% ==================================================================

:- pred resource_description(?Desc, ?Key):  resource * db_key

# "@var{Desc} is the complete description of the resource internally
identified by @var{Key}.".

resource_description(Desc, Key):-
	check_null(Desc,Key), !.
resource_description(Desc, Key):-
	resource_composition(Desc,Key,DbDesc,Needs,Provides,Asset,AssetKey,Language,LangKey,Uses,CertStatus),
	retrieve(Key,DbDesc),
	resource_uses(Key,Uses),
	resource_cert_status(Key,CertStatus),
	resource_needs(Key,Needs),
	resource_provides(Key,Provides),
	asset_description(Asset,AssetKey),
	ditem_description(Language,LangKey),
	resource_check(Desc,_Errors). % just for filling empty fields with null values.
	
:- pred resource_db_update(in(Key), in(Res)):: 
        db_key * resource => db_key * resource 

# "The database records are updated in order to make @var{Res} the new
resource description associated to @var{Key} in the database.
@var{Key} must have been previously generated and associated with a
resource.  @var{Res} must not contain free variables, and it must
abide by the integrity constraints of the ontology.  In order to
ensure that, @pred{resource_check/2} may be used before updating the
database.".

:- pred resource_db_update(-Key, in(Res)):: var * resource => db_key * resource

# "Resource @var{Res} is added to the database, and the key associated
to the resource is @var{Key}.  If @var{Res} was not present in the
database, a new @var{Key} is generated for it.  @var{Res} must not
contain free variables, and it must abide by the integrity constraints
of the ontology.  In order to ensure that, @pred{resource_check/2} may
be used before updating the database.".

resource_db_update(Key,Desc):-
	check_null(Desc,Key), !.
resource_db_update(Key, NewDesc):-
	resource_db_update_internal(Key, NewDesc),
	log_file(F),
	save_element(F,u(Key,NewDesc)),
	!.

resource_db_update_internal(Key, NewDesc):-
	ground(Key),
	db_key(Key),
	ground(NewDesc),
	!,
	resource_composition(NewDesc,Key,DbDesc,Needs,Provides,Asset,AssetKey,Language,LangKey,Uses,CertStatus),
	ditem_db_update_internal(LangKey,Language), 
	resource_db_update_uses(Key,Uses),
	resource_db_update_cert_status(Key,CertStatus),
	resource_db_update_needs(Key,Needs),
	resource_db_update_provides(Key,Provides),
	asset_db_update_internal(AssetKey,Asset),      % asset must be updated here.
	update(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

resource_db_update_internal(NewKey, NewDesc):-
	var(NewKey),
	ground(NewDesc),
	(
	    resource_description(NewDesc,NewKey)
	; 
	    resource_composition(NewDesc,NewKey,DbDesc,Needs,Provides,Asset,AssetKey,Language,LangKey,Uses,CertStatus),
	    create_key(NewKey),
	    resource_db_update_uses(NewKey,Uses),
	    resource_db_update_cert_status(NewKey,CertStatus),
	    ditem_db_update_internal(LangKey,Language), 
	    resource_db_update_needs(NewKey,Needs),
	    resource_db_update_provides(NewKey,Provides),
	    asset_db_update_internal(AssetKey,Asset),
	    insert(NewKey,DbDesc)
	),
	!.                      % just to avoid backtracking on this clause.

:- pred resource_delete(in(ResKey)): db_key

# "The resource whose associated key is @var{ResKey} is removed from
the database, along with all the information which would make the
database incoherent with respect to the constraints stated in the
ontology.".

:- pred resource_delete(in(Res)): resource 

# "The resource represented by @var{Res} is removed from the database,
along with all the information which would make the database
incoherent with respect to the constraints stated in the
ontology. @var{Res} must not contain free variables, and it must abide
by the integrity constraints of the ontology.  In order to ensure
that, @pred{resource_check/2} may be used before updating the
database.".

resource_delete(Key):-
	check_null(Key,_), !.
resource_delete(Key):-
	resource_delete_internal1(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.
resource_delete(Desc):-
	resource_delete_internal2(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.

resource_delete_internal1(Key,Desc):-
	ground(Key),
	db_key(Key),
	!,
	resource_description(Desc,Key),
	resource_composition(Desc,Key,DbDesc,_Needs,_Provides,Asset,_AssetKey,_Language,_LangKey,_Uses,_CertStatus),
	resource_db_update_uses(Key,[]),
	resource_db_update_cert_status(Key,[]),
	% cannot be deleted Language here.
%	asset_delete_internal2(_AssetKey,Asset),
	asset_delete(Asset),
	resource_db_update_needs(Key,[]),
	resource_db_update_provides(Key,[]),
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

resource_delete_internal2(Key,Res):-
	ground(Res),
	resource_description(Res,Key),
	!,
	resource_composition(Res,Key,DbDesc,_Needs,_Provides,Asset,_AssetKey,_Language,_LangKey,_Uses,_CertStatus),
	resource_db_update_uses(Key,[]),
	resource_db_update_cert_status(Key,[]),
	% cannot be deleted Language here.
%	asset_delete_internal2(AssetKey,Asset),
	asset_delete(Asset),
	resource_db_update_needs(Key,[]),
	resource_db_update_provides(Key,[]),
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

:- pred resource_needs(?Key,?Needs): db_key * list(ditem)

# "@var{Needs} is the list of dictionary entries that the package
identified by @var{Key} needs.".

:- pred resource_needs(?Res,?Needs): resource * list(ditem)

# "@var{Needs} is the list of dictionary entries that the resource
@var{Res} needs.".

:- data tmp_ditems/1.

resource_needs(Key,Needs):-
	check_null(Needs,Key), !.
% Returns the set of needs of a given key.
resource_needs(Key,_Needs):-
	ground(Key),
	db_key(Key),
	retractall_fact(tmp_ditems(_)),
	retrieve(requires(Key,ItemKey,Streaming)),
	ditem_description(Item,ItemKey),
	( null(Streaming) ->
	  FullItem = Item
	; functor(FullItem,Streaming,1),
	  arg(1,FullItem,Item)
	),
	assertz_fact(tmp_ditems(FullItem)),
	fail.
resource_needs(Key,Needs):-
	ground(Key),
	db_key(Key), !,
	findall(Item,tmp_ditems(Item),Needs),
	retractall_fact(tmp_ditems(_)).


% Returns the set of needs of a given resource (on backtracking, 
% the sets of needs of every resource).
resource_needs(Res,Needs):-
	resource(Res),
	resource_composition(Res,Key,DbDesc,Needs,_Provides,_Asset,_AssetKey,_Language,_LangKey,_Uses,_CertStatus),
	retrieve(Key,DbDesc),
	resource_requires(Res,Needs,_).

:- pred resource_provides(?Res,?Provides): resource * list(ditem)

# "@var{Provides} is the list of dictionary entries that the resource
@var{Res} holds.".

:- pred resource_provides(?Key,?Provides): db_key * list(ditem)

# "@var{Provides} is the list of dictionary entries that the resource
identified by @var{Key} provides.".

resource_provides(Key,Provides):-
	check_null(Key,Provides), !.
resource_provides(Key,_Provides):-
	ground(Key),
	db_key(Key),
	retractall_fact(tmp_ditems(_)),
	retrieve(identificationtags(Key,ItemKey,Streaming)),
	ditem_description(Item,ItemKey),
	( null(Streaming) ->
	  FullItem = Item
	; functor(FullItem,Streaming,1),
	  arg(1,FullItem,Item)
	),
	assertz_fact(tmp_ditems(FullItem)),
	fail.
resource_provides(Key,Provides):-
	ground(Key),
	db_key(Key), !,
	findall(Item,tmp_ditems(Item),Provides),
	retractall_fact(tmp_ditems(_)).

resource_provides(Res,Provides):-
	resource(Res),
	resource_composition(Res,Key,DbDesc,_Needs,Provides,_Asset,_AssetKey,_Language,_LangKey,_Uses,_CertStatus),
	retrieve(Key,DbDesc),
	resource_identificationTags(Res,Provides,_).

%% ------------------------------------------------------------------
%% Internal predicates for resource management.

:- pred resource_composition(?Res,?Key,?DbDesc,?Needs,?Provides,?Asset,?AssetKey,?Language,?LangKey):
	resource * term * list(ditem) * list(ditem)

# "Splits the resource description @var{Desc} in three different
  parts: internal database representation @var{DbDesc}, list of needed
  capabilities @var{Needs}, and provided capabilities
  @var{Provides}.".

:- comment(bug, "currently uses and cert_status are not handled").

resource_composition(Res,Key,DbDesc,Needs,Provides,Asset,AssetKey,Language,LangKey,Uses,CertStatus):-
	resource(Res),
	data_fields(resource,Arity,Fields),
	to_functor(resource/Arity,Fields,PairList,DbDesc),
	resource_update(Res,PairList,Res),
	resource_uses(Res,Uses,Res),
	resource_certification_status(Res,CertStatus,Res),
	resource_requires(Res,Needs,Res),
	resource_identificationTags(Res,Provides,Res),
	resource_asset(Res,Asset,Res),
	resource_language(Res,Language,Res),
	arg(1,DbDesc,Key),
	arg(2,DbDesc,AssetKey),
	arg(3,DbDesc,LangKey).


:- pred resource_uses(in(Key),in(Uses)): 
	db_key * list(atom)

# "Gets in @var{Uses} the records of uses of the resource with
  database key @var{Key}.".

resource_uses(Key,Uses):-
	bagof(Item,retrieve(uses(Key,_,Item)), Uses), !.
resource_uses(_Key,[]):- !.
resource_uses(_Key,Null):- null(Null), !.

:- pred resource_cert_status(in(Key),in(Uses)): 
	db_key * list(certification)

# "Gets in @var{CertStatus} the certification status of the resource
  with database key @var{Key}.".

resource_cert_status(Key,List):-
	    bagof(Item,ItemKey^(retrieve(cert_status(Key,ItemKey)),
	                certification_description(Item,ItemKey)
		       ),List), !.
resource_cert_status(_Key,[]):- !.
resource_cert_status(_Key,Null):- null(Null), !.

:- pred resource_db_update_uses(in(Key),in(Uses)): 
	db_key * list(atm)

# "Deletes all information regarding uses of resource
  @var{Key}, and inserts the uses specified in list
  @var{Uses}.".

resource_db_update_uses(Key,Uses):-
	delete_all_rows(uses(Key,_CurrentKey,_CurrentNeed)),
	insert_all_uses(Uses,uses(Key,_,_)).

insert_all_uses(Null,_):-
	null(Null).
insert_all_uses([],_).
insert_all_uses([Use|Uses],Str):-
	copy_term(Str,Str0),
	create_key(NewKey),
	arg(2,Str0,NewKey),
	arg(3,Str0,Use),
	insert(Str0),
	insert_all_uses(Uses,Str).

:- pred resource_db_update_cert_status(in(Key),in(CertStatus)): 
	db_key * list(certification)

# "Deletes all information regarding certifications status of resource
  @var{Key}, and inserts the certifications specified in list
  @var{CertStatus}.".

resource_db_update_cert_status(Key,CertStatus):-
	delete_all_rows(cert_status(Key,_CertStatus)),
	insert_all_cert_status(CertStatus,cert_status(Key,_)).

insert_all_cert_status(Null,_):-
	null(Null).
insert_all_cert_status([],_).
insert_all_cert_status([Cert|Certs],Str):-
	copy_term(Str,Str0),
	certification_db_update(Key,Cert),
	arg(2,Str0,Key),
	insert(Str0),
	insert_all_cert_status(Certs,Str).

:- pred resource_db_update_needs(in(Key),in(Needs)): 
	db_key * list(ditem)

# "Deletes all information regarding requirementes for resource
  @var{Key}, and inserts the requiremenes specified in list
  @var{Needs}.".

resource_db_update_needs(Key,Needs):-
	delete_all_rows(requires(Key,_CurrentNeed,_Streaming)),
	insert_all_ditems(Needs,requires(Key,_,_)).

:- pred resource_db_update_provides(in(Key),in(Provides)): 
	db_key * list(ditem)

# "Deletes all information regarding identification tags for resource
  @var{Key}, and inserts the identification tags specified in list
  @var{Provides}.".

resource_db_update_provides(Key,Provides):-
	delete_all_rows(identificationtags(Key,_CurrentIdTag,_Streaming)),
	insert_all_ditems(Provides,identificationtags(Key,_,_)).


%% ==================================================================

:- pred asset_description(?Desc, ?Key):  asset * db_key

# "@var{Desc} is the complete description of the asset internally
identified by @var{Key}.".

asset_description(Desc,Key):-
	check_null(Desc,Key), !.
asset_description(Desc, Key):-
	asset_composition(Desc,Key,DbDesc,Authors,Maintainers,Descriptions,
	                  Submittedby,SubmKey,Certification,CertKey),
	retrieve(Key,DbDesc),
	asset_authors(Key,Authors),
	asset_maintainers(Key,Maintainers),
	asset_descriptions(Key,Descriptions),
	user_description(Submittedby,SubmKey),
	certification_description(Certification,CertKey),
	asset_check(Desc,_Errors). % just for filling empty fields with null values.
	
:- pred asset_db_update(in(Key), in(Asset)):: 
        db_key * asset => db_key * asset

# "The database records are updated in order to make @var{Asset} the new
asset description associated to @var{Key} in the database.
@var{Key} must have been previously generated and associated with an
asset.  @var{Asset} must not contain free variables, and it must
abide by the integrity constraints of the ontology.  In order to
ensure that, @pred{asset_check/2} may be used before updating the
database.".

:- pred asset_db_update(-Key, in(Asset)):: var * asset => db_key * asset

# "Asset @var{Asset} is added to the database, and the key associated
to the asset is @var{Key}.  If @var{Asset} was not present in the
database, a new @var{Key} is generated for it.  @var{Asset} must not
contain free variables, and it must abide by the integrity constraints
of the ontology.  In order to ensure that, @pred{asset_check/2} may
be used before updating the database.".

asset_db_update(Key,Desc):-
	check_null(Key,Desc), !.
asset_db_update(Key, NewDesc):-
	asset_db_update_internal(Key, NewDesc),
	log_file(F),
	save_element(F,u(Key,NewDesc)),
	!.

asset_db_update_internal(Key, NewDesc):-
	ground(Key),
	db_key(Key),
	ground(NewDesc),
	!,
	asset_composition(_Desc,Key,DbDesc,Authors,Maintainers,Descriptions,
	                  Submittedby,SubmKey,Certification,CertKey),
	asset_db_update_authors(Key,Authors),
	asset_db_update_maintainers(Key,Maintainers),
	asset_db_update_descriptions(Key,Descriptions),
	user_db_update_internal(SubmKey,Submittedby),
	certification_db_update_internal(CertKey,Certification), 
	update(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

asset_db_update_internal(NewKey, NewDesc):-
	var(NewKey),
	ground(NewDesc),
	(
	    asset_description(NewDesc,NewKey)
	; 
	    asset_composition(NewDesc,NewKey,DbDesc,Authors,Maintainers,Descriptions,
	                      Submittedby,SubmKey,Certification,CertKey),
	    create_key(NewKey),
	    asset_db_update_authors(NewKey,Authors),
	    asset_db_update_maintainers(NewKey,Maintainers),
	    asset_db_update_descriptions(NewKey,Descriptions),
	    user_db_update_internal(SubmKey,Submittedby),
	    certification_db_update_internal(CertKey,Certification), 
	    insert(NewKey,DbDesc)
	),
	!.                      % just to avoid backtracking on this clause.

:- pred asset_delete(in(Key)): db_key

# "The asset whose associated key is @var{Key} is removed from
the database, along with all the information which would make the
database incoherent with respect to the constraints stated in the
ontology.".

:- pred asset_delete(in(Asset)): asset

# "The asset represented by @var{Asset} is removed from the database,
along with all the information which would make the database
incoherent with respect to the constraints stated in the
ontology. @var{Asset} must not contain free variables, and it must
abide by the integrity constraints of the ontology.  In order to
ensure that, @pred{asset_check/2} may be used before updating the
database.".

asset_delete(Key):-
	check_null(Key,_), !.
asset_delete(Key):-
	asset_delete_internal1(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.
asset_delete(Desc):-
	asset_delete_internal2(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.

asset_delete_internal1(Key,Desc):-
	ground(Key),
	db_key(Key),
	!,
	asset_description(Desc,Key),
	asset_composition(Desc,Key,DbDesc,_Authors,_Maintainers,_Descriptions,
	                  _Submittedby,_SubmKey,Certification,_CertKey),
	asset_db_update_authors(Key,[]),
	asset_db_update_maintainers(Key,[]),
	asset_db_update_descriptions(Key,[]),
%	user_delete(_Submittedby),          % this user could be used by other asset.
	certification_delete(Certification), 
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

asset_delete_internal2(Key,Desc):-
	ground(Desc),
	asset_description(Desc,Key),
	!,
	asset_composition(Desc,Key,DbDesc,_Authors,_Maintainers,_Descriptions,
	                  _Submittedby,_SubmKey,Certification,_CertKey),
	asset_db_update_authors(Key,[]),
	asset_db_update_maintainers(Key,[]),
	asset_db_update_descriptions(Key,[]),
%	user_delete(_Submittedby),          % this user could be used by other asset.
	certification_delete(Certification), 
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

%% ------------------------------------------------------------------
%% Internal predicates for asset management.

:- pred	asset_composition(?Desc,?Key,?DbDesc,?Authors,?Maintainers,?Descriptions,
	                  ?Submittedby,?SubmKey,?Certification,?CertKey):
	asset * db_key * term * list(user) * list(user) * list(string) *
        user * db_key * certification * db_key

# "Splits the asset description @var{Desc} in different parts:
  internal database representation @var{DbDesc}, list of authors
  @var{Authors}, list of maintainers, list of descriptions, user who
  submitted the asset, db key of that user, asset certification, and
  certification db key.".

asset_composition(Desc,Key,DbDesc,Authors,Maintainers,Descriptions,
	                  Submittedby,SubmKey,Certification,CertKey):-
	asset(Desc),
	data_fields(asset,Arity,Fields),
	to_functor(asset/Arity,Fields,PairList,DbDesc),
	asset_update(Desc,PairList,Desc),
	asset_author(Desc,Authors,Desc),
	asset_maintained_by(Desc,Maintainers,Desc),
	asset_description(Desc,Descriptions,Desc),
	asset_submitted_by(Desc,Submittedby,Desc),
	asset_certification(Desc,Certification,Desc),
	arg(1,DbDesc,Key),
	arg(2,DbDesc,SubmKey),
	arg(3,DbDesc,CertKey).

asset_authors(Key,Authors):-
	ground(Key),
	db_key(Key),
	(
	    bagof(Item,UserKey^(retrieve(authors(Key,UserKey)),
	                user_description(Item,UserKey)
		       ), Authors), !
	;
	    Authors = []
	),!.
asset_authors(_Key,Null):- null(Null),!.   % Just if it is null.

asset_maintainers(Key,Maintainers):-
	ground(Key),
	db_key(Key),
	(
	    bagof(Item,UserKey^(retrieve(maintainers(Key,UserKey)),
	                user_description(Item,UserKey)
		       ), Maintainers), !
	;
	    Maintainers = []
	),!.
asset_maintainers(_Key,Null):- null(Null),!.   % Just if it is null.

asset_descriptions(Key,Descriptions):-
	ground(Key),
	db_key(Key),
	(
	    bagof(Item,retrieve(descriptions(Key,Item)), Descriptions), !
	;
	    Descriptions = []
	),!.
asset_descriptions(_Key,Null):- null(Null),!.   % Just if it is null.

asset_db_update_authors(Key,Authors):-
	delete_all_rows(authors(Key,_CurrentAuthors)),
	user_keys(Authors,AuthKeys),
	insert_all_rows(AuthKeys,authors(Key,_)).

asset_db_update_maintainers(Key,Maintainers):-
	delete_all_rows(maintainers(Key,_CurrentIdTag)),
	user_keys(Maintainers,MaintKeys),
	insert_all_rows(MaintKeys,maintainers(Key,_)).

asset_db_update_descriptions(Key,Descriptions):-
	delete_all_rows(descriptions(Key,_CurrentDescriptions)),
	insert_all_rows(Descriptions,descriptions(Key,_)).

user_keys(Users,[]):-
	ground(Users),
	null(Users).
user_keys([],[]).
user_keys([User|Users],[Key|Keys]):-
	user_db_update_internal(Key,User),
	user_keys(Users,Keys).

%% ==================================================================

:- pred organization_description(?Desc, ?Key): organization * db_key

# "@var{Desc} is the complete description of the organization
internally identified by @var{Key}.".

organization_description(Desc,Key):-
	check_null(Desc,Key), !.
organization_description(Desc,Key):-
	organization_composition(Desc,Key,DbDesc,_Email,_Telephone,_Webpage,_Notes),
	retrieve(Key,DbDesc),
	organization_check(Desc,_Errors). % just for filling empty fields with null values.

:- pred organization_db_update(in(Key), in(Org)):: 
        db_key * organization => db_key * organization

# "The database database records are updated in order to make
@var{Org} the new organization description associated to @var{Key} in
the database. @var{Key} must have been previously generated and
associated with an organization. @var{Org} must not contain free
variables, and it must abide by the integrity constraints of the
ontology.  In order to ensure that, @pred{organization_check/2} may be
used before updating the database.".

:- pred organization_db_update(-Key, in(Org)):: 
        var * organization => db_key * organization

# "The organization @var{Org} is added to the database, and the key
associated to the organization description is @var{Key}.  If @var{Org}
was not present in the database, a new @var{Key} is generated for
it. @var{Org} must not contain free variables, and it must abide by
the integrity constraints of the ontology.  In order to ensure that,
@pred{organization_check/2} may be used before updating the
database.".

organization_db_update(Key,Desc):-
	check_null(Key,Desc), !.
organization_db_update(Key, NewDesc):-
	organization_db_update_internal(Key, NewDesc),
	log_file(F),
	save_element(F,u(Key,NewDesc)),
	!.

organization_db_update_internal(Key, NewDesc):-
	ground(Key),
	db_key(Key),
	ground(NewDesc),
	!,
	organization_composition(NewDesc,Key,DbDesc,_Email,_Telephone,_Webpage,_Notes),
	update(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.
	
organization_db_update_internal(NewKey, NewDesc):-
	var(NewKey),
	ground(NewDesc),
	(
	    organization_description(NewDesc,NewKey)
	;
	    organization_composition(NewDesc,NewKey,DbDesc,_Email,_Telephone,_Webpage,_Notes),
	    create_key(NewKey),
	    insert(NewKey,DbDesc)
	),
	!.                      % just to avoid backtracking on this clause.

:- pred organization_delete(in(OrgKey)): db_key

# "The organziation whose associated key is @var{OrgKey} is removed
from the database, along with all the information which would make the
database incoherent with respect to the constraints stated in the
ontology.".


:- pred organization_delete(in(Org)): organization

# "The organization represented by @var{Org} is removed from the
database, along with all the information which would make the database
incoherent with respect to the constraints stated in the
ontology. @var{Org} must not contain free variables, and it must abide
by the integrity constraints of the ontology.  In order to ensure
that, @pred{organization_check/2} may be used before updating the
database.".

organization_delete(Key):-
	check_null(Key,_), !.
organization_delete(Key):-
	organization_delete_internal1(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.
organization_delete(Desc):-
	organization_delete_internal2(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.

organization_delete_internal1(Key,Org):-
	ground(Key),
	db_key(Key),
	organization_description(Org,Key), 
	!,
	organization_composition(Org,Key,DbDesc,_Email,_Telephone,_Webpage,_Notes),
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

organization_delete_internal2(Key,Org):-
	ground(Org),
	organization_description(Org,Key),
	organization_composition(Org,Key,DbDesc,_Email,_Telephone,_Webpage,_Notes),
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

%% ------------------------------------------------------------------
%% Internal predicates for organization management.

:- comment(bug, "currently email, telephone, webpage and notes are not handled").

organization_composition(Org,Key,DbOrg,_Email,_Telephone,_Webpage,_Notes):-
	organization(Org),
	data_fields(organization,Arity,Fields),
	to_functor(organization/Arity,Fields,PairList,DbOrg),
	organization_update(Org,PairList,Org),
	arg(1,DbOrg,Key).

%% ==================================================================

:- pred user_description(?Desc, ?Key): user * db_key

# "@var{Desc} is the complete description of the user associated to
the identifier @var{Kay}.".

user_description(Desc,Key):-
	check_null(Desc,Key), !.
user_description(Desc,Key):-
	user_composition(Desc,Key,DbDesc,Organization,OrgKey),
	retrieve(Key,DbDesc),
	organization_description(Organization,OrgKey),
	user_check(Desc,_Errors). % just for filling empty fields with null values.

:- pred user_db_update(in(Key), in(User)):: 
        db_key * user => db_key * user 

# "The database database records are updated in order to make
@var{User} the new user description associated to @var{Key} in the
database. @var{Key} must have been previously generated and associated
with an organization.  @var{user} must not contain free variables, and
it must abide by the integrity constraints of the ontology.  In order
to ensure that, @pred{user_check/2} may be used before updating
the database.".

:- pred user_db_update(-Key, in(User))::
        var * user => db_key * user

# "The user @var{User} is added to the database, and the key
associated to the user description is @var{Key}.  If @var{User} was
not present in the database, a new @var{Key} is generated for
it. @var{user} must not contain free variables, and it must abide by
the integrity constraints of the ontology.  In order to ensure that,
@pred{user_check/2} may be used before updating the database.".

user_db_update(Key,Desc):-
	check_null(Key,Desc), !.
user_db_update(Key, NewDesc):-
	user_db_update_internal(Key, NewDesc),
	log_file(F),
	save_element(F,u(Key,NewDesc)),
	!.

user_db_update_internal(Key, NewDesc):-
	ground(Key),
	db_key(Key),
	ground(NewDesc),
	!,
	user_composition(NewDesc,Key,DbDesc,Organization,OrgKey),
	organization_db_update_internal(OrgKey,Organization),
	update(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.
	
user_db_update_internal(NewKey, NewDesc):-
	var(NewKey),
	ground(NewDesc),
	(
	    user_description(NewDesc,NewKey)
	; 
	    user_composition(NewDesc,NewKey,DbDesc,Organization,OrgKey),
	    create_key(NewKey),
	    organization_db_update_internal(OrgKey,Organization),
	    insert(NewKey,DbDesc)
	),
	!.                      % just to avoid backtracking on this clause.


:- pred user_delete(in(UserKey)): db_key

# "The user whose associated key is @var{UserKey} is removed
from the database, along with all the information which would make the
database incoherent with respect to the constraints stated in the
ontology.".


:- pred user_delete(in(User)): user

# "The user represented by @var{User} is removed from the database,
along with all the information which would make the database
incoherent with respect to the constraints stated in the
ontology. @var{User} must not contain free variables, and it must
abide by the integrity constraints of the ontology.  In order to
ensure that, @pred{user_check/2} may be used before updating the
database.".


user_delete(Key):-
	check_null(Key,_), !.
user_delete(Key):-
	user_delete_internal1(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.
user_delete(Desc):-
	user_delete_internal2(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.

user_delete_internal1(Key,Desc):-
	ground(Key),
	db_key(Key), 
	!,
	user_description(Desc,Key),
	user_composition(Desc,Key,DbDesc,_Organization,_OrgKey),
%	organization_delete(_Organization),  % this org could be used by other user.
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

user_delete_internal2(Key,User):-
	ground(User),
	user_description(User,Key),
	!,
	user_composition(User,Key,DbDesc,_Organization,_OrgKey),
%	organization_delete(_Organization),  % this org could be used by other user.
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

%% ------------------------------------------------------------------
%% Internal predicates for user management.

user_composition(User,UserKey,DbUser,Organization,OrgKey):-
	data_fields(user,Arity,Fields),
	to_functor(user/Arity,Fields,PairList,DbUser),
	user_update(User,PairList,User),
	user_affiliation(User,Organization,User),
	arg(2,DbUser,OrgKey),
	arg(1,DbUser,UserKey).

%% ==================================================================

:- pred certification_description(?Desc, ?CertKey):
        certification * db_key  

# "@var{Desc} is the complete description of the certification
associated to the identifier @var{CertKey}.".

certification_description(Desc,Key):-
	check_null(Desc,Key), !.
certification_description(Desc,Key):-
	certification_composition(Desc,Key,DbDesc),
	retrieve(Key,DbDesc),
	certification_check(Desc,_Errors). % just for filling empty fields with null values.


:- pred certification_db_update(in(Key), in(Cert))::
        db_key * certification => db_key * certification

# "The database records are updated in order to make @var{Cert} the
new certificate description associated to @var{Key} in the database.
@var{Key} must have been previously generated and associated with a
certificate.  @var{Cert} must not contain free variables, and it must
abide by the integrity constraints of the ontology.  In order to
ensure that, @pred{certification_check/2} may be used before updating
the database.".

:- pred certification_db_update(-Key, in(Cert))::
        var * certification => db_key * certification

# "The certificate @var{Cert} is added to the database, and the key
associated to the certificate is @var{Key}.  If @var{Cert} was not
present in the database, a new @var{Key} is generated for it.
@var{Cert} must not contain free variables, and it must abide by the
integrity constraints of the ontology.  In order to ensure that,
@pred{certificate_check/2} may be used before updating the database.".

certification_db_update(Key,Desc):-
	check_null(Key,Desc), !.
certification_db_update(Key, NewDesc):-
	certification_db_update_internal(Key, NewDesc),
	log_file(F),
	save_element(F,u(Key,NewDesc)),
	!.

certification_db_update_internal(Key, NewDesc):-
	ground(Key),
	db_key(Key),
	ground(NewDesc),
	!,
	certification_composition(NewDesc,Key,DbDesc),
	update(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

certification_db_update_internal(NewKey, NewDesc):-
	var(NewKey),
	ground(NewDesc),
	(
	    certification_description(NewDesc,NewKey)
	; 
	    certification_composition(NewDesc,NewKey,DbDesc),
	    create_key(NewKey),
	    insert(NewKey,DbDesc)
	),
	!.                      % just to avoid backtracking on this clause.


:- pred certification_delete(in(CertKey)): db_key 

# "The certificate whose associated key is @var{CertKey} is removed
from the database, along with all the information which would make the
database incoherent with respect to the constraints stated in the
ontology.".

:- pred certification_delete(in(Cert)): certification

# "The certificate represented by @var{Cert} is removed from the
database, along with all the information which would make the database
incoherent with respect to the constraints stated in the
ontology. @var{Cert} must not contain free variables, and it must
abide by the integrity constraints of the ontology.  In order to
ensure that, @pred{certification_check/2} may be used before updating
the database.".

certification_delete(Key):-
	check_null(Key,_), !.
certification_delete(Key):-
	certification_delete_internal1(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.
certification_delete(Desc):-
	certification_delete_internal2(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.

certification_delete_internal1(Key,Desc):-
	ground(Key),
	db_key(Key),
	!,
	certification_description(_,Key),              
	certification_composition(Desc,Key,DbDesc),
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

certification_delete_internal2(Key,Cert):-
	ground(Cert),
	certification_description(Cert,Key),
	!,
	certification_composition(Cert,Key,DbDesc),
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

%% ------------------------------------------------------------------
%% Internal predicates for certification management.

certification_composition(Desc,Key,DbDesc):-
	certification(Desc),
	data_fields(certification,Arity,Fields),
	to_functor(certification/Arity,Fields,PairList,DbDesc),
	certification_update(Desc,PairList,Desc),
	arg(1,DbDesc,Key).

%% ==================================================================

:- pred ditem_description(?Desc, ?Key): ditem * db_key

# "@var{Desc} is the description of the dictionary item @var{Item}.".

ditem_description(Desc,Key):-
	check_null(Desc,Key), !.
ditem_description(Desc,Key):-
	ditem_composition(Desc,Key,DbDesc,Synonyms,Generalization,Translation),
	retrieve(Key,DbDesc),
	ditem_synonyms(Key,Synonyms),
	ditem_generalizations(Key,Generalization),
	ditem_translations(Key,Translation),
	ditem_check(Desc,_Errors). % just for filling empty fields with null values.

:- pred ditem_db_update(in(Key), in(Item)):: 
        db_key * ditem => db_key * ditem 

# "The database records are updated in order to make @var{Item} the
new item description associated to @var{Key} in the
database. @var{Key} must have been previously generated and associated
with a dictionary item.  @var{Item} must not contain free variables,
and it must abide by the integrity constraints of the ontology.  In
order to ensure that, @pred{ditem_check/2} may be used before
updating the database.".

:- pred ditem_db_update(-Key, in(Item)):: 
        var * ditem => db_key * ditem

# "The dictionary item @var{Item} is added to the database, and the
key associated to the item description is @var{Key}.  If @var{Item}
was not present in the database, a new @var{Key} is generated for
it. @var{Item} must not contain free variables, and it must abide by
the integrity constraints of the ontology.  In order to ensure that,
@pred{ditem_check/2} may be used before updating the database.".

ditem_db_update(Key,Desc):-
	check_null(Key,Desc), !.
ditem_db_update(Key, NewDesc):-
	ditem_db_update_internal(Key, NewDesc),
	log_file(F),
	save_element(F,u(Key,NewDesc)),
	!.

ditem_db_update_internal(Key, NewDesc):-
	ground(Key),
	db_key(Key),
	ground(NewDesc),
	!,
	ditem_composition(NewDesc,Key,DbDesc,Synonyms,Generalization,Translation),
	ditem_db_update_synonyms(Key,Synonyms),
	ditem_db_update_generalization(Key,Generalization),
	ditem_db_update_translation(Key,Translation),
	update(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

ditem_db_update_internal(NewKey, NewDesc):-
	var(NewKey),
	ground(NewDesc),
	(
	    ditem_description(NewDesc,NewKey)
	; 
	    ditem_composition(NewDesc,NewKey,DbDesc,Synonyms,Generalization,Translation),
	    create_key(NewKey),
	    ditem_db_update_synonyms(NewKey,Synonyms),
	    ditem_db_update_generalization(NewKey,Generalization),
	    ditem_db_update_translation(NewKey,Translation),
	    insert(NewKey,DbDesc)
	),
	!.                      % just to avoid backtracking on this clause.

:- pred ditem_delete(+ItemKey): db_key

# "The dictionary item whose associated key is @var{ItemKey} is
removed from the database, along with all the information which would
make the database incoherent with respect to the constraints stated in
the ontology.".


:- pred ditem_delete(+Item): ditem

# "The dictionary entry represented by @var{Item} is removed from the
database, along with all the information which would make the database
incoherent with respect to the constraints stated in the
ontology. @var{Item} must not contain free variables, and it must
abide by the integrity constraints of the ontology.  In order to
ensure that, @pred{ditem_check/2} may be used before updating the
database.".

ditem_delete(Key):-
	check_null(Key,_), !.
ditem_delete(Key):-
	ditem_delete_internal1(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),
	!.
ditem_delete(Desc):-
	ditem_delete_internal2(Key,Desc),
	log_file(F),
	save_element(F,r(Key,Desc)),!.

ditem_delete_internal1(Key,Desc):-
	ground(Key),
	db_key(Key), 
	!,
	ditem_description(_,Key),
	ditem_composition(Desc,Key,DbDesc,_Synonyms,_Generalization,_Translation),
	ditem_db_update_synonyms(Key,[]),
	ditem_db_update_generalization(Key,[]),
	ditem_db_update_translation(Key,[]),
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

ditem_delete_internal2(Key,Item):-
	ground(Item),
	ditem_description(Item,Key),
	!,
	ditem_composition(Item,Key,DbDesc,_Synonyms,_Generalization,_Translation),
	ditem_db_update_synonyms(Key,[]),
	ditem_db_update_generalization(Key,[]),
	ditem_db_update_translation(Key,[]),
	delete(Key,DbDesc),
	!.                      % just to avoid backtracking on this clause.

:- pred ditem_exists(+ditem)

# "Given a ditem, success if it already exists in the database.".

ditem_exists(DItem) :-
	ditem(D),
	ditem_entry(DItem, Entry, DItem),
	ditem_generalization(DItem, Gen, DItem),
	ditem_entry(D, Entry, D),
	ditem_generalization(D, Gen, D),
	ditem_description(D,_).

%% ------------------------------------------------------------------
%% Internal predicates for ditem management. 

ditem_composition(Desc,Key,DbDesc,Synonyms,Generalization,Translation):-
	ditem(Desc),
	data_fields(ditem,Arity,Fields),
	to_functor(ditem/Arity,Fields,PairList,DbDesc),
	ditem_update(Desc,PairList,Desc),
	ditem_synonyms(Desc,Synonyms,Desc),
	ditem_generalization(Desc,Generalization,Desc),
	ditem_translation(Desc,Translation,Desc),
	arg(1,DbDesc,Key).

ditem_synonyms(Key,List):-
	    bagof(Item,retrieve(synonyms(Key,Item)), List), !.
ditem_synonyms(_Key,[]):- !.
ditem_synonyms(_Key,Null):- null(Null), !.

ditem_generalizations(Key,List):-
	    bagof(Item,ItemKey^(retrieve(generalizations(Key,ItemKey)),
	                ditem_description(Item,ItemKey)
		       ),List), !.
ditem_generalizations(_Key,[]):- !.
ditem_generalizations(_Key,Null):- null(Null), !.

ditem_translations(Key,List):-
	    bagof((Lang,Item),retrieve(translations(Key,Lang,Item)), List), !.
ditem_translations(_Key,[]):- !.
ditem_translations(_Key,Null):- null(Null),!.

ditem_db_update_synonyms(NewKey,Synonyms):-
	delete_all_rows(synonyms(NewKey,_)),
	insert_all_rows(Synonyms,synonyms(NewKey,_)).
	
ditem_db_update_generalization(NewKey,Generalization):-
	delete_all_rows(generalizations(NewKey,_)),
	insert_all_ditems(Generalization,generalizations(NewKey,_)).

ditem_db_update_translation(NewKey,Translation):-
	delete_all_rows(translations(NewKey,_,_)),
	insert_all_rows(Translation,translations(NewKey,_,_)).

%% ==================================================================
%% Common internal predicates

% Succeeds if D1 or D2 is already instantiated to null, and instantiates
% the other argument to null too. 
check_null(D1,D2):-
	ground(D1),
	null(D1),
	null(D2).
check_null(D1,D2):-
	ground(D2),
	null(D2),
	null(D1).


insert_all_rows(Null,_):-
	null(Null).
insert_all_rows([],_).
insert_all_rows([Entry|Entries],Str):-
	copy_term(Str,Str0),
	((atomic(Entry) ; string(Entry)) ->
	    arg(2,Str0,Entry)
	;
	    unify_args(Entry,1,Str0,2)
	),
	insert(Str0),
	insert_all_rows(Entries,Str).

% Unifies the args of structure Str1 with the args of Str2,
% starting with arg N1 of Str1 and arg N2 of Str2.
unify_args(Str1,N1,_Str2,_N2):-
	functor(Str1,_,N1max),
	N1 > N1max,
	!.
unify_args(_Str1,_N1,Str2,N2):-
	functor(Str2,_,N2max),
	N2 > N2max,
	!.
unify_args(Str1,N1,Str2,N2):-
	arg(N1,Str1,Arg),
	arg(N2,Str2,Arg),
	M1 is N1+1,
	M2 is N2+1,
	unify_args(Str1,M1,Str2,M2).

% Deletes all rows of a given database predicate.
delete_all_rows(Term):-
	delete(Term),
	fail.
delete_all_rows(_).

% Inserts all ditems received in first arg, using the database
% arg given as second arg.
insert_all_ditems(Null,_):-
	null(Null).
insert_all_ditems([],_).
insert_all_ditems([Ditem|Ditems],Str):-
	ditem(Ditem), !,
	ditem_db_update_internal(DitemKey,Ditem),  % If Ditem does not exist in db, inserts it;
	                                           % otherwise, returns DitemKey.
	copy_term(Str,Str0),
	arg(2,Str0,DitemKey),
	( null(Null), arg(3,Str0,Null) ; true ),   % Only if it is a three argument structure.
	insert(Str0),
	insert_all_ditems(Ditems,Str).
%% Ditem is enclosed by a streaming modifier: in(ditem), out(ditem), stream(ditem)... only for identificationTags and requires.
insert_all_ditems([DitemStream|Ditems],Str):-
	arg(1,DitemStream,Ditem),
	functor(DitemStream,Streaming,1),
	ditem(Ditem), !,
	ditem_db_update_internal(DitemKey,Ditem),  % If Ditem does not exist in db, inserts it;
	                                           % otherwise, returns DitemKey.
	copy_term(Str,Str0),
	arg(2,Str0,DitemKey),
	arg(3,Str0,Streaming),
	insert(Str0),
	insert_all_ditems(Ditems,Str).


:- pred to_functor(in(FName),in(ArgList),-PairList,?Structure):
	term * atom * list(atom) * list(pair) * term

# "@var{Structure} is an compound structure with name @var{FName} and
  arity the length of @var{ArgList}, whose arguments are the values of
  some fields of an AMOS database object indicated by
  @var{ArgList}. This predicate is reversible, and may generate either
  @var{Structure} or @var{Obj}. @var{Obj} must be instantitated to an
  AMOS object (although possibly with free variables) and, after
  calling this predicate, @tt{(object)_update(Obj,PairList,Obj)} must
  be called in order to actually perform object modification.".

:- pred to_functor(?Obj,+FNameArity,+ArgList,-PairList,?Structure):
	term * functor_arity * list(atom) * list(pair) * term

# "@var{Structure} is an compound structure with name and arity given
  by @var{FNameArity}, whose arguments are the values of the fields of
  AMOS database object @var{Obj} indicated by @var{ArgList}. If the
  arity of @var{FNameArity} is greater than the length of
  @var{ArgList}, the arguments of @var{Structure} are instantiated
  from right to left, leaving uninstantiated leftmost arguments. This
  predicate is reversible, and may generate either @var{Structure} or
  @var{Obj}. @var{Obj} must be instantitated to an AMOS object
  (although possibly with free variables) and, after calling this
  predicate, @tt{(object)_update(Obj,PairList,Obj)} must be called in
  order to actually perform object modification.".

to_functor(FNameArity,ArgList,PairList,Structure):-
	(atom(FNameArity) ->
	    FName = FNameArity,
	    Arity = ArgListLength
	;
	    FNameArity = FName/Arity
	),
	length(ArgList,ArgListLength),
	functor(Structure,FName,Arity),
	Structure =.. [FName|ValList0],
	length(ValList1,ArgListLength),
	n_tail(ValList0,ValList1),
	create_pair_list(ArgList,ValList1,PairList).

n_tail(Xs,Xs).
n_tail([_|Xs],Ys):-
	n_tail(Xs,Ys).

create_pair_list([],[],[]).
create_pair_list([A|As],[V|Vs],[A-V|Ps]):-
	create_pair_list(As,Vs,Ps).

%% ==================================================================

:- pred log_file(out(FileName)): atm

# "@var{FileName} is the name of the file on which all database
  operations are stored. To disable database logging, just comment out
  this fact, leaving log_file/1 as a data predicate with zero facts.".

:- data log_file/1.
log_file('amos_db.log').

%% ==================================================================

:- comment(version(0*1+7,2003/07/16,17:10*42+'CEST'), "Fixed bug: when
   retrieving database objects with non-used fields, these fields were
   not bound to '$null$' (they remained as uninstantiated variables)
   (Jesus Correas Fernandez)").

:- comment(version(0*1+6,2003/07/16,17:10*23+'CEST'), "Added code for
   storing and retrieving uses and cert_status lists.  (Jesus Correas
   Fernandez)").

:- comment(version(0*1+5,2003/07/04,12:36*53+'CEST'), "Changed
   use_module and include references to use directory aliases.  (Jesus
   Correas Fernandez)").

:- comment(version(0*1+4,2003/06/27,15:36*37+'CEST'), "Added log
   record for every database modification.  (Jesus Correas Fernandez)").

:- comment(version(0*1+3,2003/02/18,12:43*18+'CET'), "preliminary
   database implementation and minor documentation changes (Jesus
   Correas Fernandez)").

:- comment(version(0*1+2,2003/02/17,16:01*32+'CET'), "Documentation
   and minor changes on module interface.  (Jesus Correas
   Fernandez)").

:- comment(version(0*1+1,2003/02/14,15:56*45+'CET'), "Initial version,
   with all interfaces included.  (Jesus Correas Fernandez)").

:- comment(version_maintenance,on).

